import type { Command } from './commands/Command';
import type { Editor } from './Editor';

class History {
  editor: Editor;
  undos: Command[];
  redos: Command[];
  lastCmdTime: number;
  idCounter: number;
  historyDisabled: boolean;
  config: typeof Editor.prototype.config;
  constructor(editor: Editor) {
    this.editor = editor;
    this.undos = [];
    this.redos = [];
    this.lastCmdTime = Date.now();
    this.idCounter = 0;

    this.historyDisabled = false;
    this.config = editor.config;

    this.editor.signals.startPlayer.add(() => {
      this.historyDisabled = true;
    });

    this.editor.signals.stopPlayer.add(() => {
      this.historyDisabled = false;
    });
  }

  execute(cmd: Command, optionalName?: string) {
    const lastCmd = this.undos[this.undos.length - 1];
    const timeDifference = Date.now() - this.lastCmdTime;

    const isUpdatableCmd =
      lastCmd?.updatable &&
      cmd.updatable &&
      lastCmd.object === cmd.object &&
      lastCmd.type === cmd.type &&
      lastCmd.attributeName === cmd.attributeName;

    if (isUpdatableCmd && timeDifference < 500) {
      lastCmd.update(cmd);
      cmd = lastCmd;
    } else {
      // the command is not updatable and is added as a new part of the history
      this.undos.push(cmd);
      cmd.id = ++this.idCounter;
    }

    cmd.name = optionalName !== undefined ? optionalName : cmd.name;
    cmd.execute();

    this.lastCmdTime = Date.now();

    // clearing all the redo-commands
    this.redos = [];
    this.editor.signals.historyChanged.dispatch(cmd);
  }

  undo() {
    if (this.historyDisabled) {
      return;
    }

    const cmd = this.undos.pop();

    if (cmd) {
      cmd.undo();
      this.redos.push(cmd);
      this.editor.signals.historyChanged.dispatch(cmd);
    }

    return cmd;
  }

  redo() {
    if (this.historyDisabled) {
      return;
    }

    const cmd = this.redos.pop();

    if (cmd) {
      cmd.execute();
      this.undos.push(cmd);
      this.editor.signals.historyChanged.dispatch(cmd);
    }

    return cmd;
  }

  clear() {
    this.undos = [];
    this.redos = [];
    this.idCounter = 0;

    this.editor.signals.historyChanged.dispatch();
  }
}

export { History };
